package com.hjw.httpclient.async;

import com.hjw.httpclient.async.util.HttpClientUtils;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.util.EntityUtils;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeoutException;

/**
 * 自定义FutureCallback的实现类，实现请求重试策略
 * 静态代理的模式，实现请求重试
 * 重试：
 * 1.超时异常，执行failed回调函数，需要进行重试
 * 2.成功和服务器建立TCP连接，但是服务器出错响应状态码500，需要重试
 * 释放锁的地方：
 * 1.请求成功释放锁
 * 2.重试次数达到上限释放锁
 *
 * @author hjwStrart
 * @time 2022-11-30 20:31
 * @version: 1.0
 */
@Slf4j
public class MyRetryProxy implements FutureCallback<HttpResponse> {

    /**
     * 最大重试次数
     */
    private final int maxRetryCount;
    /**
     * 当前重试次数
     */
    private int currentRetryCount;
    /**
     * 封装请求信息，用来执行重试
     */
    private HttpUriRequest request; // 用来发送重试请求
    /**
     * 自定义的接口
     * 1.简化回调函数
     * 2.复用Entity
     */
    private CallBack callBack; // 自定义的接口，目的是用来简化回调函数，主要实现类MyCallBack实现了Entity的复用
    /**
     * 全局锁资源，必须等到所有请求成功响应(或者达到最大重试次数)，
     */
    private final CountDownLatch countDownLatch;  // 控制主线程的运行


    public MyRetryProxy(HttpUriRequest request, CallBack callBack, CountDownLatch countDownLatch) {
        this(-1, request, callBack, countDownLatch);
    }

    public MyRetryProxy(int maxRetryCount, HttpUriRequest request, CallBack callBack, CountDownLatch countDownLatch) {
        if (maxRetryCount < 1) {
            this.maxRetryCount = -1;
        } else {
            this.maxRetryCount = maxRetryCount;
        }

        this.request = request;
        this.callBack = callBack;
        this.countDownLatch = countDownLatch;
    }

    @Override
    public void completed(HttpResponse httpResponse) {
        System.out.println("completed");
        int statusCode = httpResponse.getStatusLine().getStatusCode();
        HttpEntity entity = httpResponse.getEntity();
        String result = null;
        try {
            if (statusCode == 200) {
                // 获取到响应体内容
                result = EntityUtils.toString(entity);
                // 提高result的作用域，变相复用了entity
                callBack.success(result);
                countDownLatch.countDown();
                log.info("{}：释放锁，当前锁数量：{}", request.getURI(), countDownLatch.getCount());
            } else {
                doRetry();
            }
        } catch (IOException e) {
            e.printStackTrace();
            doRetry();
        }
    }

    @Override
    public void failed(Exception e) {
        System.out.println("failed");
        callBack.fail(null);
        if (e instanceof TimeoutException) {
            doRetry();
        }
    }

    @Override
    public void cancelled() {
    }

    public void doRetry() {
        currentRetryCount++;
        log.info("{}:对{}执行第{}次重试", Thread.currentThread().getName(), request.getURI(), this.currentRetryCount);
        if (maxRetryCount == -1 || currentRetryCount >= maxRetryCount) {
            System.out.println("uri:" + request.getURI() + ",已达到重试上限，maxRetryCount:" + maxRetryCount);
            System.out.println("doRetry:::释放countDownLatch锁资源");
            countDownLatch.countDown();
        } else {
            HttpClientUtils.doGet(request, this);
        }
    }

}
